Link
Description
记字符串的倒置为 wR。例如 (abcd)R=dcba。
“双倍回文”为形如 wwRwwR 的字符串。
给定一个字符串 s,求 s 的最长的双倍回文子串长度。
Solution
观察双倍回文的性质,发现双倍回文整体是一个回文串,它的一半也是一个回文串。考虑找到所有满足条件的双倍回文。
对于每个 x,找到最远的 y,其中 x 为双倍回文字串的中点,y 为右端点。
答案即为 max{(y−x)×2}
对于每对 (x,y),需要满足
fx≥2×(y−x)
fy≥y−x
其中 fi 表示以 i 为回文中心的最长回文串长度,用 manacher 预处理即可。
整理上面的式子可得
y≤x+2fx
fy−y≥x
所以我们把 fy−y 排个序,这样枚举 x 时,y 只会变多,不会减少,开个 set 维护一下,将当前满足条件1的 y 加入 set,再二分找答案。
注意双倍回文要保证 fx 为偶数,并且 y−x 也为偶数。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <iostream> #include <cstdio> #include <set> #include <algorithm>
using namespace std;
const int N = 2e6 + 5; int n; char s[N]; int f[N], ans; struct node { int val, id; friend bool operator < (node x, node y) { return x.val < y.val; } }a[N]; set <int> S;
void read(char s[]) { s[0] = '@', s[1] = '#'; n = 1; char c = getchar(); while(c < 'a' || c > 'z') c = getchar(); while(c >= 'a' && c <= 'z') s[++n] = c, s[++n] = '#', c = getchar(); return; }
void manacher() { for(int i = 1, r = 0, mid = 0; i <= n; i++) { if(i <= r) f[i] = min(f[(mid << 1) - i], r - i + 1); while(s[i + f[i]] == s[i - f[i]]) f[i]++; if(i + f[i] > r) r = i + f[i] - 1, mid = i; } return; }
void solve() { for(int i = 1; i <= n; i++) f[i]--, a[i].val = i - f[i], a[i].id = i; sort(a + 1, a + 1 + n); for(int i = 1, j = 1; i <= n; i++) { while(j <= n && a[j].val <= i) S.insert(a[j].id), j++; if(f[i] & 1) continue; auto pos = --S.upper_bound(i + f[i] / 2); if((*pos - i) % 2 == 0) ans = max(ans, (*pos - i) << 1); } printf("%d\n", ans); return; }
int main() { scanf("%d", &n); read(s); manacher(); solve(); return 0; }
|